package com.cy.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.sys.pojo.SysLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.util.ParameterTypes;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Date;

/**
 * Spring框架中由@Aspect注解描述的类型为切面类型，此切面类型中要有
 * 1)切入点的定义 (用于约束在哪些目标方法执行时，进行功能拓展)
 * 2)通知方法的定义(这样的方法中会封装要执行的扩展业务逻辑，例如日志记录)
 */
@Slf4j
@Aspect
@Component
public class SysLogAspect {
    /**
     * @Pointcut注解用于定义切入点
     * @annotation(注解类全名) 为定义切入点的一种表达式，由表达式中注解描述
     * 的方法为切入点方法，可以在目标业务执行时通过指定的通知方法进行功能增强
     */
    @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
    public void doLog(){}//此方法只负责承载切入点，不需要写方法实现

    /**
     * @Around 注解描述的方法为一个用于执行拓展业务逻辑的方法，此方法中
     * 可以通过连接点对象(joinPoint)调用目标方法。
     * @param joinPoint 表示一个连接点对象，此对象封装了切入点方法信息(目标方法信息)，
     *                  可以通过连接点对象调用切入点方法(目标方法)
     * @return 目标方法的返回结果。
     */
    @Around("doLog()")//@Around("@annotation(com.cy.pj.common.annotation.RequiredLog)")
    public Object doLogAround(ProceedingJoinPoint joinPoint)
            throws Throwable{
        try {
            //System.out.println("before:"+System.currentTimeMillis());
            log.info("before {}", System.currentTimeMillis());
            Object result = joinPoint.proceed();//调用切入点方法
            //System.out.println("after:"+System.currentTimeMillis());
            log.info("after {}", System.currentTimeMillis());
            //记录详细的正常行为日志
            return result;//此返回值会交给代理对象，代理对象传给调用方法
        }catch(Throwable e){
            e.printStackTrace();
            log.error("exception {}",System.currentTimeMillis());
            //记录详细的异常行为日志
            throw e;
        }
    }
    //记录详细的用户操作日志
    private void doLogInfo(ProceedingJoinPoint joinPoint,long time,Throwable e) throws Exception {
        //1.获取日志
        //1.1获取登录用户名 (还没做登录时，先给假数据)
        String username="tony";
        //1.2获取ip(不会获取ip地址时先给假数据)
        String ip="202.106.29.20";
        Class<?> targetCls=joinPoint.getTarget().getClass();
        MethodSignature ms=(MethodSignature)joinPoint.getSignature();
        Method targetMethod=targetCls.getDeclaredMethod(ms.getName(),ms.getParameterTypes());
        RequiredLog requiredLog=targetMethod.getAnnotation(RequiredLog.class);
        String operation=requiredLog.operation();
        //1.3获取操作
        //1.4获取方法信息
        //1.5获取方法参数
        //2.封装日志
        SysLog userLog=new SysLog();
        userLog.setUsername(username);
        userLog.setIp(ip);
//        userLog.setOperation();//获取切入点方法上RequiredLog注解中operation属性值
//        userLog.setMethod();//获取切入点方法的信息(方法所在的类+方法名)
//        userLog.setParams();//执行方法时调用的参数
//        userLog.setStatus();//操作状态
//        userLog.setError();//错误信息
//        userLog.setTime();
        userLog.setCreatedTime(new Date());
        //3.记录日志
        log.info("user.oper {}",userLog.toString());
    }

}
